# ~*~ coding: utf-8 ~*~
import traceback

from flask import request, views, render_template, jsonify, current_app
from apps.common.utils import fill_date_to_timestamp, timestamp_obj_to_str, list2dict
from apps.common.mixin import CompatibleWithSelect2Mixin
from apps.common import response
from apps.extensions import db
from apps.perms.mixin import PermissionRequiredMixin, LoginRequiredMixin
from ..models import Asset, AdminUser, AssetNode, Node
from ..forms import AssetCreateUpdateForm
from ..tasks import update_asset_hardware_info_manual, test_assets_user_connectivity_manual

__all__ = [
    'AssetListAPI', 'AssetListView', 'AssetCreateView', 'AssetUpdateView', 'AssetDeleteView',
    'AssetConnectivityView', 'AssetHardwareView'
]


class AssetListAPI(CompatibleWithSelect2Mixin, PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'assets-asset-list'

    def get(self):
        create_time = request.args.get('create_time')
        start_time = end_time = None
        if create_time:
            start_time = fill_date_to_timestamp('start', create_time.split('~')[0])
            end_time = fill_date_to_timestamp('end', create_time.split('~')[1])

        filter_conditions = {
            'and': [
                ('create_time', 'gte', start_time),
                ('create_time', 'lte', end_time),
            ],
            'or': [
                ('hostname', 'like', request.args.get('search')),
            ],
        }

        # 排序
        sort = request.args.get('sort') or 'create_time'
        order = request.args.get('order') or 'desc'
        order_by_list = [(sort, order), ]

        # 分页
        offset = request.args.get('offset') or 0
        limit = request.args.get('limit') or 12

        # 节点过滤
        node_id = request.args.get('node')
        queryset = db.session.query(Asset)  # 默认列出所有的资产
        if node_id:
            node = db.session.query(Node).filter(Node.id == node_id).first()
            if node.is_root:
                queryset = db.session.query(Asset)  # 根节点列出所有的资产
            else:
                children = node.get_all_children(with_self=True)    # 根节点根据权限列出自己的资产
                node_ids = [c.id for c in children]
                assets = db.session.query(AssetNode.asset_id).filter(AssetNode.node_id.in_(node_ids)).all()
                if list(zip(*assets)):
                    assets = set(list(zip(*assets))[0])
                queryset = queryset.filter(Asset.id.in_(assets))

        # Compatible with select2
        if request.values.get('type') == 'select2':
            filter_conditions, order_by_list, offset, limit = self.select2_queryset()

        assets, count = Asset.get_queryset(queryset=queryset,
                                           filter_conditions=filter_conditions,
                                           order_by_list=order_by_list,
                                           offset=offset, limit=limit)
        # 获取关联的管理用户
        asset_ids = [asset.id for asset in assets]
        admin_user = db.session.query(Asset.id, AdminUser.name).\
            join(AdminUser, AdminUser.id == Asset.admin_user_id).\
            filter(Asset.id.in_(asset_ids)).\
            all()
        admin_user_dict = list2dict(admin_user)

        results = list()
        for asset in assets:
            results.append({
                'id': asset.id,
                'hostname': asset.hostname,
                'manage_ip': asset.manage_ip,
                'admin_user': admin_user_dict.get(asset.id) if admin_user_dict.get(asset.id) else '',
                'hardware_info': asset.hardware_info,
                'connectivity': asset.connectivity.display(),
                'create_time': timestamp_obj_to_str(asset.create_time),
            })

        resp = {
            "data": {
                "rows": results,
                "total": count
            }
        }
        return jsonify(resp)


class AssetListView(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'assets-asset-list'

    def get(self):
        return render_template('assets/asset_list.html')


class AssetUpdateView(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'assets-asset-update'

    def get(self, pk):
        instance = db.session.query(Asset).get(pk)
        node_list = db.session.query(Node).join(AssetNode, Node.id == AssetNode.node_id).filter(AssetNode.asset_id == pk).all()

        nodes = []
        for node in node_list:
            nodes.append({
                'id': node.id,
                'value': node.value
            })

        content = {
            'id': instance.id,
            'hostname': instance.hostname,
            'platform': instance.platform,
            'protocol': instance.protocol,
            'port': instance.port,
            'manage_ip': instance.manage_ip,
            'public_ip': instance.public_ip,
            'admin_user_id': instance.admin_user_id,
            'nodes': nodes
        }
        return render_template('assets/asset_update.html', **content)

    def post(self, pk):
        instance = db.session.query(Asset).get(str(pk))
        form = AssetCreateUpdateForm(request.values)
        if form.validate_on_submit():
            _clean_data = form.data
            if _clean_data.get('hostname') != instance.hostname:
                if db.session.query(Asset).filter_by(hostname=_clean_data.get('hostname')).count():
                    return response.params_error({
                        'field': 'hostname',
                        'message': '主机名称不能重复'
                    })

            nodes = _clean_data.pop('nodes')
            labels = _clean_data.pop('labels')

            asset = instance.update(**_clean_data, commit=False)
            db.session.flush()
            # 先删除
            db.session.query(AssetNode).filter(AssetNode.asset_id == asset.id).delete()
            # 后增加
            asset_nodes = []
            for node_id in nodes.split(','):
                asset_nodes.append(AssetNode(asset_id=asset.id, node_id=node_id))
            db.session.bulk_save_objects(asset_nodes)
            db.session.commit()
            return response.success()
        else:
            return response.params_error(form.get_error())


class AssetCreateView(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'assets-asset-create'

    def get(self):
        return render_template('assets/asset_create.html')

    def post(self):
        form = AssetCreateUpdateForm(request.values)
        if form.validate_on_submit():
            _clean_data = form.data
            if db.session.query(Asset).filter_by(hostname=_clean_data.get('hostname')).count():
                return response.params_error({
                    'field': 'hostname',
                    'message': '主机名称不能重复'
                })

            nodes = _clean_data.pop('nodes')
            labels = _clean_data.pop('labels')

            asset = Asset.create(**_clean_data, commit=False)
            db.session.flush()
            asset_nodes = []
            for node_id in nodes.split(','):
                asset_nodes.append(AssetNode(asset_id=asset.id, node_id=node_id))
            db.session.bulk_save_objects(asset_nodes)
            db.session.commit()

            update_asset_hardware_info_manual.apply_async(args=(asset,), serializer='pickle')
            test_assets_user_connectivity_manual.apply_async(args=(asset,), serializer='pickle')
            return response.success()
        else:
            return response.params_error(form.get_error())


class AssetDeleteView(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'assets-asset-delete'

    def post(self, pk):
        db.session.query(Asset).filter(Asset.id == pk).delete()
        db.session.query(AssetNode).filter(AssetNode.asset_id == pk).delete()
        db.session.commit()
        return response.success()


class AssetConnectivityView(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'assets-asset-connectivity'

    def post(self, pk):
        try:
            instance = db.session.query(Asset).get(pk)
            task = test_assets_user_connectivity_manual.apply_async(args=(instance,), serializer='pickle')
            return response.success(data={'task_id': task.id})
        except Exception as e:
            current_app.logger.error(traceback.format_exc())
            return response.server_error(data={'message': '服务器出现未知错误，请联系管理员！'})


class AssetHardwareView(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'assets-asset-hardware'

    def post(self, pk):
        try:
            instance = db.session.query(Asset).get(pk)
            task = update_asset_hardware_info_manual.apply_async(args=(instance,), serializer='pickle')
            return response.success(data={'task_id': task.id})
        except Exception as e:
            current_app.logger.error(traceback.format_exc())
            return response.server_error(data={'message': '服务器出现未知错误，请联系管理员！'})
